package org.hismig.migration;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.sql.DataSource;

import org.hismig.cfg.Configuration;
import org.hismig.cfg.DataBase;
import org.hismig.cfg.DynamicPartition;
import org.hismig.cfg.ExtExecutor;
import org.hismig.cfg.ParamBuilder;
import org.hismig.cfg.Strategy;
import org.hismig.cfg.Table;
import org.hismig.cfg.Trigger;
import org.hismig.migration.executor.DBExecutor;
import org.hismig.migration.executor.TableExecutor;
import org.hismig.migration.executor.ext.IExtExecutor;
import org.hismig.migration.parambuilder.IParamBuilder;
import org.hismig.migration.strategy.IStrategy;
import org.hismig.partition.IDynamicPartition;
import org.hismig.scheduler.ITrigger;
import org.hismig.utils.CheckTool;
import org.hismig.utils.ClassLoaderTool;
import org.hismig.utils.DateTool;
import org.hismig.utils.JDBCTool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @declare: This class to construct the implements's instance from the
 *           configuration <br />
 *           This class created by Vansion.Hopihit on 2012-07-04 <br />
 *           <a href="mailto:vansionhopihit@gmail.com">Contact Me</a>
 * @version 1.0
 * @author Vansion.Hopihit
 * 
 */
public class Constructor {

	private static Logger logger = LoggerFactory.getLogger(Constructor.class);

	private ITrigger trigger;
	private IStrategy strategy;
	private List<DBExecutor> dbExecutors;

	/**
	 * init method call to construct the trigger,the strategy, and the
	 * dbExecutors
	 * 
	 * @param configuration
	 */
	public void init(Configuration configuration) {
		CheckTool.checkNotNull(configuration);

		trigger = getTrigger(configuration.getTrigger());
		CheckTool.checkNotNull(trigger);

		strategy = getStrategy(configuration.getStrategy());
		CheckTool.checkNotNull(strategy);

		dbExecutors = getDBExecutors(configuration.getDatabases());
	}

	private List<DBExecutor> getDBExecutors(List<DataBase> databases) {
		List<DBExecutor> dbExecutors = new ArrayList<DBExecutor>();

		if (null != databases) {
			for (DataBase database : databases) {
				DBExecutor dbExecutor = new DBExecutor();

				dbExecutor.setSourceDataSource(JDBCTool.getDataSource(database
						.getSourceDBData().getDbConnection()));

				dbExecutor.setDistDataSource(JDBCTool.getDataSource(database
						.getDistDBData().getDbConnection()));

				// in common, the dynamicPartition is use for the distDatabase
				dbExecutor.setDynamicPartition(getDynamicPartition(database
						.getDistDBData().getDynamicPartition()));

				dbExecutor.setParamBuilders(getParamBuilders(database
						.getParamBuilders()));

				dbExecutor.setSourcePreExtExecutor(setExtExecutorProp(
						getExtExecutor(database.getSourcePreExtExecutor()),
						dbExecutor.getSourceDataSource()));

				dbExecutor.setDistPreExtExecutor(setExtExecutorProp(
						getExtExecutor(database.getDistPreExtExecutor()),
						dbExecutor.getDistDataSource(),
						dbExecutor.getDynamicPartition()));

				dbExecutor.setDistAfterExtExecutor(setExtExecutorProp(
						getExtExecutor(database.getDistAfterExtExecutor()),
						dbExecutor.getDistDataSource(),
						dbExecutor.getDynamicPartition()));

				dbExecutor.setSourceAfterExtExecutor(setExtExecutorProp(
						getExtExecutor(database.getSourceAfterExtExecutor()),
						dbExecutor.getSourceDataSource()));

				dbExecutor.setTableExecutors(getTableExecutors(
						database.getTables(), dbExecutor));

				dbExecutor.setDataBase(database);

				dbExecutors.add(dbExecutor);
			}
		}

		return dbExecutors;
	}

	private IExtExecutor setExtExecutorProp(IExtExecutor extExecutor,
			DataSource dataSource, IDynamicPartition... dynamicPartitions) {
		if (null != extExecutor) {
			extExecutor.setDataSource(dataSource);

			if (dynamicPartitions.length > 0) {
				extExecutor.setDynamicPartition(dynamicPartitions[0]);
			}
		}

		return extExecutor;
	}

	private List<TableExecutor> getTableExecutors(List<Table> tables,
			DBExecutor dbExecutor) {
		List<TableExecutor> tableExecutors = new ArrayList<TableExecutor>();
		if (null != tables) {
			for (Table table : tables) {

				TableExecutor tableExecutor = new TableExecutor();

				tableExecutor.setParamBuilders(getParamBuilders(table
						.getParamBuilders()));

				tableExecutor.setSourcePreExtExecutor(setExtExecutorProp(
						getExtExecutor(table.getSourcePreExtExecutor()),
						dbExecutor.getSourceDataSource()));

				tableExecutor.setDistPreExtExecutor(setExtExecutorProp(
						getExtExecutor(table.getDistPreExtExecutor()),
						dbExecutor.getDistDataSource(),
						dbExecutor.getDynamicPartition()));

				tableExecutor.setDistAfterExtExecutor(setExtExecutorProp(
						getExtExecutor(table.getDistAfterExtExecutor()),
						dbExecutor.getDistDataSource(),
						dbExecutor.getDynamicPartition()));

				tableExecutor.setSourceAfterExtExecutor(setExtExecutorProp(
						getExtExecutor(table.getSourceAfterExtExecutor()),
						dbExecutor.getSourceDataSource()));

				tableExecutor.setTable(table);

				tableExecutor.setDbExecutorBase(dbExecutor);
				tableExecutor.setStrategy(strategy);

				tableExecutors.add(tableExecutor);
			}
		}

		return tableExecutors;
	}

	private IDynamicPartition getDynamicPartition(
			DynamicPartition dynamicPartition) {
		if (null != dynamicPartition) {
			String className = dynamicPartition.getClassName();
			CheckTool.checkNotEmpty(className);
			Object impl = ClassLoaderTool.load(className);

			if (impl instanceof IDynamicPartition) {
				IDynamicPartition iDynamicPartition = (IDynamicPartition) impl;
				iDynamicPartition.setDynamicPartition(dynamicPartition);

				return iDynamicPartition;
			} else {
				logger.warn(String.format(
						"Can't load the DynamicPartition from [Class:%s]",
						className));
			}

		}

		return null;
	}

	private IExtExecutor getExtExecutor(ExtExecutor extExecutor) {

		if (null != extExecutor) {

			String className = extExecutor.getClassName();
			CheckTool.checkNotEmpty(className);

			Object impl = ClassLoaderTool.load(className);

			if (impl instanceof IExtExecutor) {

				IExtExecutor iextExecutor = (IExtExecutor) impl;
				iextExecutor.setExtExecutor(extExecutor);

				return iextExecutor;
			} else {
				logger.warn(String
						.format("Can't load the ExtExecutor from [Class:%s]",
								className));
			}

		}

		return null;
	}

	private ITrigger getTrigger(Trigger trigger) {
		if (null != trigger) {
			String className = trigger.getClassName();
			CheckTool.checkNotEmpty(className);

			Object impl = ClassLoaderTool.load(className);

			if (impl instanceof ITrigger) {
				ITrigger iTrigger = (ITrigger) impl;
				iTrigger.setTrigger(trigger);

				return iTrigger;
			} else {
				logger.warn(String.format(
						"Can't load the Trigger from [Class:%s]", className));
			}
		}

		return null;
	}

	private IStrategy getStrategy(Strategy strategy) {

		if (null != strategy) {

			String className = strategy.getClassName();
			CheckTool.checkNotEmpty(className);

			Object impl = ClassLoaderTool.load(className);

			if (impl instanceof IStrategy) {

				IStrategy iStrategy = (IStrategy) impl;
				iStrategy.setStrategy(strategy);

				return iStrategy;
			} else {
				logger.warn(String.format(
						"Can't load the Strategy from [Class:%s]", className));
			}

		}

		return null;
	}

	private List<IParamBuilder> getParamBuilders(
			List<ParamBuilder> paramBuilders) {
		List<IParamBuilder> iparambuilders = new ArrayList<IParamBuilder>();

		if (null != paramBuilders) {

			for (ParamBuilder paramBuilder : paramBuilders) {

				String className = paramBuilder.getClassName();
				CheckTool.checkNotEmpty(className);

				Object impl = ClassLoaderTool.load(className);

				if (impl instanceof IParamBuilder) {

					IParamBuilder iparamBuilder = (IParamBuilder) impl;
					iparamBuilder.setParamBuilder(paramBuilder);

					iparambuilders.add(iparamBuilder);
				} else {
					logger.warn(String.format(
							"Can't load the ParamBuilder from [Class:%s]",
							className));
				}
			}
		}

		return iparambuilders;
	}

	/**
	 * initContext method call to init the context with normal model
	 * 
	 * @return
	 */
	public Context initContext() {
		return initContext(null);
	}

	/**
	 * initContext method call to init the context with the exist context
	 * 
	 * @param context
	 * @return
	 */
	public Context initContext(Context context) {
		if (null == context) {
			context = new Context();
		}

		// set the common context
		context.set(Context.KEY_CURRENTDAY, DateTool.getNormalDate(new Date()));

		return context;
	}

	public ITrigger getTrigger() {
		return trigger;
	}

	public IStrategy getStrategy() {
		return strategy;
	}

	public List<DBExecutor> getDbExecutors() {
		return dbExecutors;
	}

}
